﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

using Db4o.Framework.Interfaces;
    
namespace Db4o.Framework.Business
{
    public sealed class BusinessFactory : ILogged
    {
        #region Design pattern : << Singleton BusinessFactory >>

        // read-only private member
        private static readonly BusinessFactory instance = new BusinessFactory();

        // private construtor
        private BusinessFactory() { }

        // public property
        public static BusinessFactory Instance { get { return instance; } }

        #endregion

        #region GetInstance methods
        /// <summary>
        /// Create an instance of a managed object
        /// </summary>
        /// <param name="type">type wanted</param>
        /// <returns>DbEntity objec</returns>
        public ManagedType CreateInstance(string type)
        {
            // get All the parameters types to get the write construtor
            StringBuilder logmsg = new StringBuilder();
            Type t = BusinessManager.Instance.GetType(type);
            // get the default ctor
            ConstructorInfo ctor = t.GetConstructor(new Type[0]);


            ManagedType obj = (ManagedType)ctor.Invoke(new object[0]);
       

            return obj;
        }

        public ManagedType CreateInstance(Type type)
        {
            return CreateInstance(type.Name);
        }
        /// <summary>
        /// Create an instance of a managed object
        /// </summary>
        public ManagedType CreateInstance(string type, params object[] args)
        {
            try
            {
                // get All the parameters types to get the write construtor
                StringBuilder logmsg = new StringBuilder();
                Type t = BusinessManager.Instance.GetType(type);

                // get the specific ctor
                Type[] paramTypes = new Type[args.Length];
                foreach (ConstructorInfo ctor in t.GetConstructors())
                {
                    int i = -1;
                    // check for the parameter types
                    foreach (ParameterInfo p in ctor.GetParameters())
                    {
                        i++;
                        // args must be in the same place as ctor parameters
                        if (p.ParameterType == args[i].GetType())
                            paramTypes[i] = args[i].GetType();
                        else
                            break; // try another ctor
                    }
                    // check the param to be sure all param types are specified
                    for (int stop = 0; stop < args.Length; stop++)
                    {
                        if (paramTypes[stop] == null)
                            break;
                        else
                            // we can invoke
                            if (stop == args.Length - 1)
                            {
                                ManagedType obj = (ManagedType)ctor.Invoke(args);
                                return obj;
                            }
                    }
                }
            }
            catch (Exception e)
            {
                throw e;
            }
            return null;
        } 
	    #endregion

        #region CreateInstanceWithId
        /// <summary>
        /// Create and Store a managedType object with default constructor
        /// </summary>
        /// <param name="type">type wanted</param>
        /// <returns>DbEntity object</returns>
        public ManagedType CreateInstanceWithId(Type type)
        {
            try
            {
                // get the default ctor
                ConstructorInfo ctor = type.GetConstructor(new Type[0]);

                ManagedType obj = (ManagedType)ctor.Invoke(new object[0]);
                DataManager.Instance.Store(obj);
                return obj;
            }
            catch (Exception e)
            {
                logger.FatalFormat("{0} - {1}\n{2}", e.Source, e.Message, e.StackTrace);
                throw e;
            }
        }
        /// <summary>
        /// Create and Store a managedType object with specific constructor
        /// </summary>
        /// <param name="typeName">type name</param>
        /// <param name="args">constructor args</param>
        /// <returns>DbEntity object</returns>
        public ManagedType CreateInstanceWithId(string typeName, params object[] args)
        {       
            try
            {
                ManagedType obj = CreateInstance(typeName, args);
                // store the object to get an DB4O unique Id
                DataManager.Instance.Store(obj);
                return obj;
            }
            catch (Exception e)
            {
                throw e;
            }
            return null;
            
        }

        /// <summary>
        /// Create and Store a managedType object with specific constructor
        /// </summary>
        /// <param name="type">type wanted</param>
        /// <param name="args">constructor args</param>
        /// <returns>DbEntity object</returns>
        public ManagedType CreateInstanceWithId(Type type, params object[] args)
        {
            return CreateInstanceWithId(type.Name, args);
        } 
        #endregion

    }
}
